package top.dyzmj.microbench.util;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import top.dyzmj.detty.core.utils.Attribute;
import top.dyzmj.detty.core.utils.AttributeKey;
import top.dyzmj.detty.core.utils.DefaultAttributeMap;
import top.dyzmj.microbench.microbench.AbstractMicrobenchmark;

import java.util.IdentityHashMap;

/**
 * 描述:
 *
 * @author dongYu
 * @date 2021/11/23
 */
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@State(Scope.Benchmark)
public class DefaultAttributeMapBenchmark extends AbstractMicrobenchmark {

	@Param({ "8", "32", "128" })
	private int keyCount;
	private AttributeKey<Integer>[] keys;
	private IdentityHashMap<AttributeKey<Integer>, Attribute<Integer>> identityHashMap;
	private DefaultAttributeMap attributes;

	@State(Scope.Thread)
	public static class KeySequence {

		long nextKey;

		@Setup(Level.Iteration)
		public void reset() {
			nextKey = 0;
		}

		public long next() {
			return nextKey++;
		}
	}

	@Setup
	public void init() {
		if (Integer.bitCount(keyCount) != 1) {
			throw new AssertionError("keyCount should cbe a power of 2");
		}
		attributes = new DefaultAttributeMap();
		keys = new AttributeKey[keyCount];
		identityHashMap = new IdentityHashMap<>(keyCount);
		for (int i = 0; i < keyCount; i++) {
			final AttributeKey<Integer> key = AttributeKey.valueOf(Integer.toString(i));
			keys[i] = key;
			final Attribute<Integer> attribute = attributes.attr(key);
			identityHashMap.put(key, attribute);
		}
	}

	@Benchmark
	@Threads(3)
	public Attribute<Integer> nextAttributeIdentityHashMap(KeySequence sequence) {
		long next = sequence.next();
		AttributeKey<Integer>[] keys = this.keys;
		AttributeKey<Integer> key = keys[(int) (next & keys.length - 1)];
		return identityHashMap.get(key);
	}

	@Benchmark
	@Threads(3)
	public boolean hasAttributeIdentityHashMap(KeySequence sequence) {
		long next = sequence.next();
		AttributeKey<Integer>[] keys = this.keys;
		AttributeKey<Integer> key = keys[(int) (next & keys.length - 1)];
		return identityHashMap.containsKey(key);
	}

	@Benchmark
	@Threads(3)
	public void mixedAttributeIdentityHashMap(KeySequence sequence, Blackhole hole) {
		long next = sequence.next();
		AttributeKey<Integer>[] keys = this.keys;
		AttributeKey<Integer> key = keys[(int) (next & keys.length - 1)];
		if (next % 2 == 0) {
			hole.consume(identityHashMap.get(key));
		} else {
			hole.consume(identityHashMap.containsKey(key));
		}
	}

	@Benchmark
	@Threads(3)
	public Attribute<Integer> nextAttributeAttributeMap(KeySequence sequence) {
		long next = sequence.next();
		AttributeKey<Integer>[] keys = this.keys;
		AttributeKey<Integer> key = keys[(int) (next & keys.length - 1)];
		return attributes.attr(key);
	}

	@Benchmark
	@Threads(3)
	public boolean nextHasAttributeAttributeMap(KeySequence sequence) {
		long next = sequence.next();
		AttributeKey<Integer>[] keys = this.keys;
		AttributeKey<Integer> key = keys[(int) (next & keys.length - 1)];
		return attributes.hasAttr(key);
	}

	@Benchmark
	@Threads(3)
	public void mixedAttributeAttributeMap(KeySequence sequence, Blackhole hole) {
		long next = sequence.next();
		AttributeKey<Integer>[] keys = this.keys;
		AttributeKey<Integer> key = keys[(int) (next & keys.length - 1)];
		if (next % 2 == 0) {
			hole.consume(attributes.attr(key));
		} else {
			hole.consume(attributes.hasAttr(key));
		}
	}
}
